/*
 * Copyright 2000-2006 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intellij.gwt.actions;

import com.intellij.CommonBundle;
import com.intellij.gwt.GWTFileScanner;
import com.intellij.gwt.GwtConfiguration;
import com.intellij.gwt.GwtBundle;
import com.intellij.gwt.module.GwtModulesManager;
import com.intellij.gwt.module.model.GwtModule;
import com.intellij.ide.IdeView;
import com.intellij.ide.actions.CreateElementActionBase;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateManager;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.javaee.LibrariesManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataConstants;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.io.File;
import java.util.Properties;
import java.util.List;
import java.util.ArrayList;

public abstract class GwtNewActionBase extends CreateElementActionBase {
  private static final Logger LOG = Logger.getInstance("#com.intellij.gwt.actions.GwtNewActionBase");
  @NonNls private static final String NAME_TEMPLATE_PROPERTY = "NAME";

  public GwtNewActionBase(String text, String description, Icon icon) {
    super(text, description, icon);
  }

  @NotNull
  protected final PsiElement[] invokeDialog(final Project project, final PsiDirectory directory) {
    if (!GwtConfiguration.getInstance().isValidConfiguration()) {
      ShowSettingsUtil.getInstance().editConfigurable(project, GwtConfiguration.getInstance());
    }

    if (!GwtConfiguration.getInstance().isValidConfiguration()) return PsiElement.EMPTY_ARRAY;

    if (requireGwtModule()) {
      final GwtModule gwtModule = findGwtModule(project, directory);
      if (gwtModule == null) {
        final String message = GwtBundle.message("error.message.this.action.is.allowed.only.for.client.side.packages.of.a.gwt.module");
        Messages.showErrorDialog(project, message, CommonBundle.getErrorTitle());
        return PsiElement.EMPTY_ARRAY;
      }
    }

    MyInputValidator validator = new MyInputValidator(project, directory);
    Messages.showInputDialog(project, getDialogPrompt(), getDialogTitle(), Messages.getQuestionIcon(), "", validator);

    PsiElement[] createdElements = validator.getCreatedElements();
    if (createdElements.length > 0) {
      ApplicationManager.getApplication().runWriteAction(new Runnable() {
        public void run() {
          new GWTClasspathUpdaterFileScanner(directory.getVirtualFile()).scanProject(project);
        }
      });
    }
    return createdElements;
  }

  protected PsiFile[] getAffectedFiles(final GwtModule gwtModule) {
    return PsiFile.EMPTY_ARRAY;
  }

  protected abstract boolean requireGwtModule();

  protected abstract String getDialogPrompt();

  protected abstract String getDialogTitle();

  protected static @Nullable GwtModule findGwtModule(Project project, PsiDirectory directory) {
    return GwtModulesManager.getInstance(project).findGwtModuleByClientSourceFile(directory.getVirtualFile());
  }

  public final void update(final AnActionEvent e) {
    final Presentation presentation = e.getPresentation();

    if (requireGwtSupport() && !GwtConfiguration.getInstance().isValidConfiguration()) {
      if (presentation.isEnabled()) {
        presentation.setEnabled(false);
        presentation.setVisible(false);
      }
      return;
    }

    super.update(e);

    if (presentation.isEnabled() && !isUnderSourceRoots(e)) {
      presentation.setEnabled(false);
      presentation.setVisible(false);
    }
  }

  public static boolean isUnderSourceRoots(final AnActionEvent e) {
    final DataContext context = e.getDataContext();
    Module module = (Module)context.getData(DataConstants.MODULE);
    if (module == null) {
      return false;
    }
    final IdeView view = (IdeView)context.getData(DataConstants.IDE_VIEW);
    final Project project = (Project)context.getData(DataConstants.PROJECT);
    if (view != null && project != null) {
      ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
      PsiDirectory[] dirs = view.getDirectories();
      for (PsiDirectory dir : dirs) {
        if (projectFileIndex.isInSourceContent(dir.getVirtualFile()) && dir.getPackage() != null) {
          return true;
        }
      }
    }

    return false;
  }

  protected boolean requireGwtSupport() {
    return true;
  }

  @NotNull
  protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception {
    final GwtModule gwtModule;
    if (requireGwtModule()) {
      gwtModule = findGwtModule(directory.getProject(), directory);
    }
    else {
      gwtModule = null;
    }
    return doCreate(newName, directory, gwtModule);
  }

  @NotNull
  protected abstract PsiElement[] doCreate(String newName, PsiDirectory directory, final GwtModule gwtModule) throws Exception;

  protected static PsiClass createClassFromTemplate(final PsiDirectory directory, String className, String templateName,
                                                    @NonNls String... parameters) throws IncorrectOperationException {
    final PsiFile file = createFromTemplateInternal(directory, className, className + ".java", templateName, parameters);
    return ((PsiJavaFile)file).getClasses()[0];
  }

  protected static PsiFile createFromTemplate(final PsiDirectory directory, String fileName, String templateName,
                                              @NonNls String... parameters) throws IncorrectOperationException {
    return createFromTemplateInternal(directory, FileUtil.getNameWithoutExtension(fileName), fileName, templateName, parameters);
  }

  protected static PsiFile createFromTemplateInternal(final PsiDirectory directory, final String name, String fileName, String templateName,
                                                      @NonNls String... parameters) throws IncorrectOperationException {
    final FileTemplate template = FileTemplateManager.getInstance().getJ2eeTemplate(templateName);

    Properties properties = new Properties(FileTemplateManager.getInstance().getDefaultProperties());
    FileTemplateUtil.setPackageNameAttribute(properties, directory);
    properties.setProperty(NAME_TEMPLATE_PROPERTY, name);
    LOG.assertTrue(parameters.length % 2 == 0);
    for (int i = 0; i < parameters.length; i+=2) {
      properties.setProperty(parameters[i], parameters[i + 1]);
    }
    String text;
    try {
      text = template.getText(properties);
    }
    catch (Exception e) {
      throw new RuntimeException("Unable to load template for " + FileTemplateManager.getInstance().internalTemplateToSubject(templateName), e);
    }

    final PsiManager psiManager = PsiManager.getInstance(directory.getProject());
    final PsiFile file = psiManager.getElementFactory().createFileFromText(fileName, text);

    CodeStyleManager.getInstance(psiManager).reformat(file);

    return (PsiFile)directory.add(file);
  }


  protected String getErrorTitle() {
    return CommonBundle.getErrorTitle();
  }

  protected final void checkBeforeCreate(String newName, PsiDirectory directory) throws IncorrectOperationException {
    doCheckBeforeCreate(newName, directory);
    List<VirtualFile> files = new ArrayList<VirtualFile>();
    for (PsiFile psiFile : getAffectedFiles(findGwtModule(directory.getProject(), directory))) {
      final VirtualFile virtualFile = psiFile.getVirtualFile();
      if (virtualFile != null) {
        files.add(virtualFile);
      }
    }
    ReadonlyStatusHandler.getInstance(directory.getProject()).ensureFilesWritable(files.toArray(new VirtualFile[files.size()]));
  }

  protected void doCheckBeforeCreate(String newName, PsiDirectory directory) throws IncorrectOperationException {
    directory.checkCreateClass(newName);
  }

  static class GWTClasspathUpdaterFileScanner extends GWTFileScanner {
    VirtualFile directory;

    GWTClasspathUpdaterFileScanner(VirtualFile directory) {
      this.directory = directory;
    }

    public void scanRoot(Module module, VirtualFile root) {
      if (VfsUtil.isAncestor(root, directory, false)) {
        LibrariesManager libManager = LibrariesManager.getInstance();
        ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel();
        LibraryTable libs = model.getModuleLibraryTable();

        for (Library lib : libs.getLibraries()) {
          if (libManager.isClassAvailableInLibrary(lib, "com.google.gwt.user.client.DOM")) return;
        }

        Library library = libs.createLibrary();
        Library.ModifiableModel modifiableModel = library.getModifiableModel();
        String urlForLibraryRoot = VfsUtil.getUrlForLibraryRoot(new File(GwtConfiguration.getInstance().getUserJarPath()));
        modifiableModel.addRoot(urlForLibraryRoot, OrderRootType.CLASSES);
        modifiableModel.addRoot(urlForLibraryRoot, OrderRootType.SOURCES);
        modifiableModel.commit();
        model.commit();
      }
    }
  }
}
